#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jni.h>
#include "icu_lingjie_jni_LingJieByCl.h"
#include <CL/cl.h>
#include <windows.h>

////////////////////// OpenCL ////////////////

#define ARRAY_SIZE	(512)
#define MY_DEBUG	1

// 一些方法声明
cl_platform_id* getPlatforms();		// 获取平台信息
cl_device_id* getDevices();			// 获取设备信息
char* getSource();					// 获取内核代码
cl_context getContext();			// 获取上下文
cl_command_queue getCommandQueue(); // 获取命令队列
cl_program getPrograme();			// 获取程序
cl_kernel getKernel(char* name);	// 获取内核函数
cl_mem getIntBuffer(int* , int);	// 获取int缓冲区
cl_mem getCharBuffer(char*, int);	// 获取char缓冲区
cl_mem getFloatBuffer(float*, int );	// 获取float缓冲区
void clean();						// 清理
void loadMapFromFile();				// 加载地图
void CreateMemObjects(float* a, float* b);

// 其他，有的用了windows的一些API
void printTime();					// 打印时间
void sleep(int ms);					// 睡眠
void debugPrint(char*);				// 打印字符串

// CL 全局变量
cl_platform_id* platform;			// 平台
cl_device_id* device;				// 设备
cl_context context;					// 上下文
cl_command_queue queue;				// 命令队列
cl_program program;					// 程序
cl_kernel calcAccKernel;			// 计算速度的函数
cl_kernel movePointKernel;			// 移动粒子
cl_mem QL_ACCS_BUFFER;				// 加速度缓冲区
cl_mem QL_MAP_BUFFER;				// 地图缓冲区
cl_mem QL_FMAP_BUFFER;				// 力图缓冲区

// 重要的全局变量
char* QL_FILE_PATH;					// 地图文件
int QL_WIDTH, QL_HEIGHT;			// 世界的宽高
int QL_CELLS_COUNT;					// 宽 * 高
int QL_WIDTH_POWER, QL_HEIGHT_POWER;// 宽高的幂
int QL_WIDTH_MASK, QL_HEIGHT_MASK;	// 111111..11
const int QL_F_MASK = (1 << 4) - 1;	// 1111
float* QL_ACCS;						// 加速度常量数组
int QL_ACCS_LENGTH;					// 数组的长度
int* QL_MAP;						// 地图
char* QL_FMAP;						// 力图
int QL_PLANTFORM_IDX;				// 第几个平台
int QL_DEVICE_IDX;					// 第几个设备
int QL_GLOBAL_WORK_SIZE;			// 刷新地图时，将地图总共分为多少列
int QL_LOCAL_WORK_SIZE;				// 每个工作组处理多少列
int QL_V_FACTOR;					// 速度的衰减因子（千分之几）

// 计算数组内的索引
#define getIdx(x,y) ((x&QL_WIDTH_MASK)+((y&QL_HEIGHT_MASK)<<QL_WIDTH_POWER))

// 初始化
int init()
{
	debugPrint("init, 初始化");

	// 加载地图文件
	loadMapFromFile();

	// 使用第一个平台
	platform = getPlatforms() + QL_PLANTFORM_IDX;
	// 使用第一个设备
	device = getDevices() + QL_DEVICE_IDX;
	// 上下文
	context = getContext();
	// 命令队列
	queue = getCommandQueue();
	// 程序
	program = getPrograme();
	// 函数
	calcAccKernel = getKernel("calcAcc");
	movePointKernel = getKernel("movePoint");
	// 缓冲区
	QL_ACCS_BUFFER = getFloatBuffer(QL_ACCS, QL_ACCS_LENGTH);
	QL_MAP_BUFFER = getIntBuffer(QL_MAP, QL_CELLS_COUNT);
	QL_FMAP_BUFFER = getCharBuffer(QL_FMAP, QL_CELLS_COUNT);



	/* */
	return 0;
}

// 演化，生成下一张图
void evolution() {

	cl_int err;					// 执行是否错误
	size_t globalWorkSize[1] = { QL_GLOBAL_WORK_SIZE };		// 全局工作项的数量
	size_t localWorkSize[1] = { QL_LOCAL_WORK_SIZE };		// 每个工作组有多少个工作项

	// 设置内核参数
	err = clSetKernelArg(calcAccKernel, 0, sizeof(cl_mem), &QL_ACCS_BUFFER);
	err |= clSetKernelArg(calcAccKernel, 1, sizeof(cl_mem), &QL_MAP_BUFFER);
	err |= clSetKernelArg(calcAccKernel, 2, sizeof(cl_mem), &QL_FMAP_BUFFER);
	if (err != CL_SUCCESS) {
		printf("Error setting kernel calcAccKernel arguments.\n");
		exit(1);
	}
	err = clSetKernelArg(movePointKernel, 0, sizeof(cl_mem), &QL_MAP_BUFFER);
	if (err != CL_SUCCESS) {
		printf("Error setting kernel movePointKernel arguments.\n");
		exit(1);
	}

	// 拷贝力图到 GPU
	debugPrint("copy FMAP to GPU, 拷贝力图到 GPU ...");
	err = clEnqueueWriteBuffer(queue, QL_FMAP_BUFFER, CL_TRUE, 0, QL_CELLS_COUNT * sizeof(char), QL_FMAP, 0, NULL, NULL);
	memset(QL_FMAP, 0, sizeof(char) * QL_CELLS_COUNT);
	debugPrint("copy FMAP to GPU, 拷贝力图到 GPU, OK");

	// 执行内核，计算速度，（维度是一维的，每个工作项计算一列）
	debugPrint("run calcAccKernel, 执行calcAccKernel， ...");
	err = clEnqueueNDRangeKernel(queue, calcAccKernel, 1, NULL, globalWorkSize, localWorkSize, 0, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("Error queueing kernel calcAccKernel\n");
		exit(1);
	}
	err = clFinish(queue);
	if (err != CL_SUCCESS) {
		printf("Error run kernel calcAccKernel! 执行出错！\n");
		exit(1);
	}
	debugPrint("run calcAccKernel, 执行calcAccKernel， OK");

	// 执行内核，移动粒子
	debugPrint("run movePointKernel, 执行movePointKernel， ...");
	err = clEnqueueNDRangeKernel(queue, movePointKernel, 1, NULL, globalWorkSize, localWorkSize, 0, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("Error queueing kernel movePointKernel\n");
		exit(1);
	}
	err = clFinish(queue);
	if (err != CL_SUCCESS) {
		printf("Error run kernel movePointKernel! 执行出错！\n");
		exit(1);
	}
	debugPrint("run movePointKernel, 执行movePointKernel， OK");


	// 将计算的结果拷贝到主机上
	debugPrint("copy GPU MAP to host，拷贝GPU中的地图 ...");
	err = clEnqueueReadBuffer(queue, QL_MAP_BUFFER, CL_TRUE, 0, QL_CELLS_COUNT * sizeof(int), QL_MAP, 0, NULL, NULL);
	if (err != CL_SUCCESS) {
		printf("ERROR: copy GPU MAP to host，拷贝GPU中的地图失败！\n");
		exit(1);
	}
	debugPrint("copy GPU MAP to host，拷贝GPU中的地图， OK");

	for (int i = 0; i < 4; i++) {
		printf("\t%d : %d\n", i, QL_MAP[i]);
	}
	debugPrint("Executed program succesfully\n");
	sleep(1);

}

// 加载地图
void loadMapFromFile() {
	FILE* fp = NULL;
	printTime();
	printf("loadMapFromFile 加载地图文件： %s,start...\n", QL_FILE_PATH);
	// 1. 打开文件
	errno_t err = fopen_s(&fp, QL_FILE_PATH, "rb");
	if (err != 0) {
		printf("file open error, 读取文件失败！, %s, errCode=%d\n", QL_FILE_PATH, err);
		exit(0);
	}
	// 2. 读取文件
	size_t readSize = fread(QL_MAP, sizeof(int), QL_CELLS_COUNT, fp);
	if (readSize != QL_CELLS_COUNT) {
		printf("WARNING, fileSize=%zu, mapCells=%d, not equal, 文件大小和世界大小不匹配 \n", readSize, QL_CELLS_COUNT);
	}
	printf("\t");
	for (int i = 0; i < QL_CELLS_COUNT && i<16; i++) {
		printf("%d,", QL_MAP[i]);
	}
	printf("\n");
	// 3. 关闭文件
	fclose(fp);
	printTime();
	printf("loadMapFromFile 加载地图文件： %s,OK\n", QL_FILE_PATH);
}

// 保存到文件
void saveToFile() {
	FILE* fp = NULL;
	printTime();
	printf("save to file 保存地图到文件： %s,start...\n", QL_FILE_PATH);

	// 1. 打开文件
	errno_t err = fopen_s(&fp, QL_FILE_PATH, "wb");
	if (err != 0) {
		printf("file open error, 打开文件失败！, %s, errCode=%d\n", QL_FILE_PATH, err);
		exit(0);
	}
	// 2. 写入文件
	size_t readSize = fwrite(QL_MAP, sizeof(int), QL_CELLS_COUNT, fp);
	if (readSize != QL_CELLS_COUNT) {
		printf("WARNING, fileSize=%zu, mapCells=%d, not equal, 文件大小和世界大小不匹配 \n", readSize, QL_CELLS_COUNT);
	}
	// 3. 关闭文件
	fclose(fp);
	printTime();
	printf("save to file 保存地图到文件： %s,OK\n", QL_FILE_PATH);
}



// 获取平台信息
cl_platform_id* getPlatforms() {
	printTime();
	printf("platform infos，平台信息:");
	cl_int err;					// 执行是否错误

	// 1. 获取平台个数
	cl_uint num_platform;		// 平台个数
	err = clGetPlatformIDs(0, NULL, &num_platform);
	if (err != CL_SUCCESS) {
		printf("get platform failed, 获取平台失败！\n");
		exit(1);
	}
	cl_platform_id* platform = malloc(sizeof(cl_platform_id) * num_platform);
	// 2. 遍历平台信息
	err = clGetPlatformIDs(num_platform, platform, NULL);
	for (cl_uint i = 0; i < num_platform; i++) {
		printf("\n\tPlatform %d information\n", i);
		size_t size;
		// 平台名字
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_NAME, NULL, NULL, &size);
		char* PName = (char*)malloc(size);
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_NAME, size, PName, NULL);
		printf("\t\tCL_PLATFORM_NAME: %s\n", PName);
		// 开发商
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_VENDOR, 0, NULL, &size);
		char* PVendor = (char*)malloc(size);
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_VENDOR, size, PVendor, NULL);
		printf("\t\tCL_PLATFORM_VENDOR: %s\n", PVendor);
		// 最大 OpenCL 版本
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_VERSION, 0, NULL, &size);
		char* PVersion = (char*)malloc(size);
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_VERSION, size, PVersion, NULL);
		printf("\t\tCL_PLATFORM_VERSION: %s\n", PVersion);
		// 全部支持还是部分支持
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_PROFILE, 0, NULL, &size);
		char* PProfile = (char*)malloc(size);
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_PROFILE, size, PProfile, NULL);
		printf("\t\tCL_PLATFORM_PROFILE: %s\n", PProfile);
		// 支持的扩展列表
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_EXTENSIONS, 0, NULL, &size);
		char* PExten = (char*)malloc(size);
		err = clGetPlatformInfo(platform[i], CL_PLATFORM_EXTENSIONS, size, PExten, NULL);
		printf("\t\tCL_PLATFORM_EXTENSIONS: %s\n", PExten);
		// 释放内存
		free(PName);
		free(PVendor);
		free(PVersion);
		free(PProfile);
		free(PExten);
	}

	return platform;
}

// 获取设备信息
cl_device_id* getDevices() {
	debugPrint("device infos，设备信息:");
	cl_int err;

	// 1. 获取设备
	cl_uint deviceNum;
	err = clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_GPU, 0, NULL, &deviceNum);
	if (err != CL_SUCCESS) {
		printf("get GPU device failed, 获取GPU设备失败！\n");
		exit(1);
	}
	cl_device_id* devices = (cl_device_id*)malloc(sizeof(cl_device_id) * deviceNum);
	clGetDeviceIDs(platform[0], CL_DEVICE_TYPE_GPU, deviceNum, devices, NULL);

	// 2. 打印设备信息
	for (cl_uint i = 0; i < deviceNum; i++) {
		printTime();
		printf("\n\tDevice %d information，设备信息：\n", i);
		size_t size;
		cl_uint uNum;
		cl_ulong uLong;
		// 设备名称
		err = clGetDeviceInfo(devices[i], CL_DEVICE_NAME, 0, NULL, &size);
		char* chars = (char*)malloc(size);
		err = clGetDeviceInfo(devices[i], CL_DEVICE_NAME, size, chars, NULL);
		printf("\t\t CL_DEVICE_NAME : %s\n", chars);
		free(chars);
		// 计算单元最大数量
		err = clGetDeviceInfo(devices[i], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), &uNum, NULL);
		printf("\t\t CL_DEVICE_MAX_COMPUTE_UNITS 计算单元（CU数） : %d\n", uNum);
		// 最大维度
		err = clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(cl_uint), &uNum, NULL);
		printf("\t\t CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 最大维度 : %d\n", uNum);
		// 每个维度的工作项最大数量
		size_t* itemSizes = (size_t*)malloc(uNum * sizeof(size_t));
		// err = clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_ITEM_SIZES, uNum * sizeof(size_t), itemSizes, NULL);
		// printf("\t CL_DEVICE_MAX_WORK_ITEM_SIZES : ");
		// for (int j = 0; i < uNum; i++) {
		// 	printf("%u\t", itemSizes[j]);
		// }
		// free(itemSizes);
		// printf("###\n");
		// 每个计算单元上执行内核的一个工作组内的最大工作项个数
		err = clGetDeviceInfo(devices[i], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &size, NULL);
		printf("\t\t CL_DEVICE_MAX_WORK_GROUP_SIZE : %zu\n", size);
		// 核心频率
		err = clGetDeviceInfo(devices[i], CL_DEVICE_MAX_CLOCK_FREQUENCY, sizeof(cl_uint), &uNum, NULL);
		printf("\t\t CL_DEVICE_MAX_CLOCK_FREQUENCY : %d\n", uNum);
		// 全局内存大小
		err = clGetDeviceInfo(devices[i], CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &uLong, NULL);
		printf("\t\t CL_DEVICE_GLOBAL_MEM_SIZE : %0.0f MB\n", (float)uLong / 1024 / 1024);
		// 全局内存缓存行
		err = clGetDeviceInfo(devices[i], CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, sizeof(cl_uint), &uNum, NULL);
		printf("\t\t CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE : %d\n", uNum);
		// OpenCL 版本
		err = clGetDeviceInfo(devices[i], CL_DEVICE_VERSION, 0, NULL, &size);
		chars = (char*)malloc(size);
		err = clGetDeviceInfo(devices[i], CL_DEVICE_VERSION, size, chars, NULL);
		printf("\t\t CL_DEVICE_VERSION : %s\n", chars);
		free(chars);
		// 支持的扩展
		err = clGetDeviceInfo(devices[i], CL_DEVICE_EXTENSIONS, 0, NULL, &size);
		chars = (char*)malloc(size);
		err = clGetDeviceInfo(devices[i], CL_DEVICE_EXTENSIONS, size, chars, NULL);
		printf("\t\t CL_DEVICE_EXTENSIONS : %s\n", chars);
		free(chars);



	}

	return devices;
}

// 获取上下文
cl_context getContext() {
	printTime();
	printf("getContext，创建上下文\n");
	cl_int err;
	cl_context context = clCreateContext(NULL, 1, device, NULL, NULL, &err);
	if (err != CL_SUCCESS) {
		printf("Create context error\n");
		return NULL;
	}
	return context;
}

// 在上下文可用的第一个设备中创建命令队列
cl_command_queue getCommandQueue() {
	debugPrint("在上下文可用的第一个设备中创建命令队列\n");
	cl_command_queue commandQueue = NULL;
	// commandQueue = clCreateCommandQueue(context, device[0], 0, NULL);
	// OpenCL 2.0 的用法
	commandQueue = clCreateCommandQueueWithProperties(context, device[0], 0, NULL);
	if (commandQueue == NULL) {
		printf("Failed to create commandQueue for device 0\n");
		return NULL;
	}
	return commandQueue;
}

// 创建程序
cl_program getPrograme() {
	debugPrint("创建程序对象\n");
	cl_int err;
	cl_program program;
	//记录大小的数据类型
	char* const source = getSource();
	program = clCreateProgramWithSource(context, 1, (const char**)&source, NULL, NULL);

	if (program == NULL) {
		printf("Failed to creae CL program from source.\n");
		return NULL;
	}
	char option[4096];
	sprintf_s(option, sizeof(option), " -D WIDTH=%d -D HEIGHT=%d -D WIDTH_MASK=%d -D HEIGHT_MASK=%d -D WIDTH_POWER=%d -D HEIGHT_POWER=%d -D CELLS_COUNT=%d -D V_FACTOR=%ff ",
		QL_WIDTH, QL_HEIGHT, QL_WIDTH_MASK, QL_HEIGHT_MASK, QL_WIDTH_POWER, QL_HEIGHT_POWER, QL_CELLS_COUNT, (float)QL_V_FACTOR/1000);
	printTime();
	printf("program options=%s\n", option);
	err = clBuildProgram(program, 0, NULL, option, NULL, NULL);
	if (err != CL_SUCCESS) {
		char buildLog[16384];
		clGetProgramBuildInfo(program, device[0], CL_PROGRAM_BUILD_LOG, sizeof(buildLog), buildLog, NULL);
		printf("Error in kernel : %s \n", buildLog);
		clReleaseProgram(program);
		return NULL;
	}
	return program;
}

// 创建内存对象
cl_mem getIntBuffer(int* intArr, int length) {
	printTime();
	printf("create int buffer, 创建int类型的缓冲区, length=%d\n", length);
	cl_mem mem = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(int) * length, intArr, NULL);
	if (mem == NULL) {
		printf("ERROR: create int buffer error, 创建int类型的缓冲区失败！\n");
		exit(1);
	}
	return mem;
}
cl_mem getCharBuffer(char* charArr, int length) {
	printTime();
	printf("create char buffer, 创建char类型的缓冲区, length=%d\n", length);
	cl_mem mem = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(char) * length, charArr, NULL);
	if (mem == NULL) {
		printf("ERROR: create char buffer error, 创建char类型的缓冲区失败！\n");
		exit(1);
	}
	return mem;
}
cl_mem getFloatBuffer(float* floatArr, int length) {
	printTime();
	printf("create float buffer, 创建float类型的缓冲区, length=%d\n", length);
	cl_mem mem = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR, sizeof(float) * length, floatArr, NULL);
	if (mem == NULL) {
		printf("ERROR: create float buffer error, 创建float类型的缓冲区失败！\n");
		exit(1);
	}
	return mem;
}


// 创建内核函数
cl_kernel getKernel(char* kernelName) {
	printTime();
	printf("创建内核函数 %s\n", kernelName);
	// 创建内核
	cl_kernel kernel = clCreateKernel(program, kernelName, NULL);

	// 查询内核参数
	cl_int err;
	size_t size;
	// 最大全局工作项大小
	size_t itemSizes[3];
	err = clGetKernelWorkGroupInfo(kernel, device[0], CL_KERNEL_GLOBAL_WORK_SIZE, sizeof(size_t)*3, itemSizes, NULL);
	printf("\t CL_KERNEL_GLOBAL_WORK_SIZE 最大全局工作项大小 : %zu, %zu, %zu\n", itemSizes[0], itemSizes[1], itemSizes[2]);
	// 最大工作组大小
	err = clGetKernelWorkGroupInfo(kernel, device[0], CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &size, NULL);
	printf("\t CL_KERNEL_WORK_GROUP_SIZE 最大工作组大小 : %zu \n", size);
	// 推荐的工作组大小倍数
	err = clGetKernelWorkGroupInfo(kernel, device[0], CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, sizeof(size_t), &size, NULL);
	printf("\t CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 推荐的工作组大小倍数(处理单元， CU中的PE数) : %zu \n", size);

	return kernel;
}


// 清理资源
void clean() {
	printTime();
	printf("clean, 清理资源, ...\n");
	if (QL_ACCS_BUFFER != NULL) {
		clReleaseMemObject(QL_ACCS_BUFFER);
	}
	if (QL_MAP_BUFFER != NULL) {
		clReleaseMemObject(QL_MAP_BUFFER);
	}
	if (QL_FMAP_BUFFER != NULL) {
		clReleaseMemObject(QL_FMAP_BUFFER);
	}
	if (queue != NULL) {
		clReleaseCommandQueue(queue);
		queue = NULL;
	}
	if (calcAccKernel != NULL) {
		clReleaseKernel(calcAccKernel);
		calcAccKernel = NULL;
	}
	if (movePointKernel != NULL) {
		clReleaseKernel(movePointKernel);
		movePointKernel = NULL;
	}
	if (program != NULL) {
		clReleaseProgram(program);
		program = NULL;
	}
	if (context != NULL) {
		clReleaseContext(context);
		context = NULL;
	}
	if (QL_MAP != NULL) {
		free(QL_MAP);
		QL_MAP = NULL;
	}
	if (QL_FMAP != NULL) {
		free(QL_FMAP);
		QL_FMAP = NULL;
	}
	printTime();
	printf("clean, 清理资源, OK\n");
}


char* ReadKernelSourceFile() {
	const char* filename = "D:/MyCode/lingjieWeb/lingjie.cl";
	FILE* file = NULL;
	size_t sourceLenth;
	char* sourceString;
	int ret;
	errno_t err = fopen_s(&file, filename, "rb");
	if (err != 0) {
		printf("file open error, 读取文件失败！, %s, errCode=%d\n", filename, err);
		exit(0);
	}
	if (file == NULL) {
		printf("%s at %d : Can't open %s \n", __FILE__, __LINE__ - 2, filename);
		return NULL;
	}
	//重定位到文件末尾
	fseek(file, 0, SEEK_END);
	sourceLenth = ftell(file);
	//重定位到文件开头
	fseek(file, 0, SEEK_SET);
	sourceString = (char*)malloc(sourceLenth + 1);
	sourceString[0] = '\0';
	ret = fread(sourceString, sourceLenth, 1, file);
	if (ret == 0) {
		printf("%s at %d : Cant't read source %s\n", __FILE__, __LINE__ - 2, filename);
		return NULL;
	}
	fclose(file);
	sourceString[sourceLenth] = '\0';
	return sourceString;


}

// 获取内核代码
char* getSource() {

	return ReadKernelSourceFile();
}





////////////////////// 其他 /////////////////

// 打印时间
void printTime() {
	SYSTEMTIME sys;
	GetLocalTime(&sys);
	printf("%4d-%02d-%02d %02d:%02d:%02d.%03d ",
		sys.wYear, sys.wMonth, sys.wDay, sys.wHour, sys.wMinute, sys.wSecond, sys.wMilliseconds);
}

// 睡眠
void sleep(int ms) {
	Sleep(ms);
}

// 把一个jstring转换成- -个c语言的char*类型.
char* _JString2CStr(JNIEnv* env, jstring jstr) {
	char* rtn = NULL;
	jclass clsString = (*env)->FindClass(env, "java/lang/String");
	jstring strEncode = (*env)->NewStringUTF(env, "UTF-8");
	jmethodID mid = (*env)->GetMethodID(env, clsString, "getBytes", "(Ljava/lang/String;)[B");
	jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strEncode); // String.getBytes("UTF-8" );
	jsize alen = (*env)->GetArrayLength(env, barr);
	jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
	if (alen > 0) {
		rtn = (char*)malloc(alen + 1); //"\0”
		memcpy(rtn, ba, alen);
		rtn[alen] = '\0';
	}
	(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
	return rtn;
}

// 调试的时候，打印字符串
void debugPrint(char* msg) {
	if (MY_DEBUG) {
		printTime();
		printf("%s\n", msg);
	}
}


////////////////////// Java ////////////////



/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    init		初始化地图
 * Signature: (Ljava/lang/String;II[F[I)I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_init
(JNIEnv* env, jobject self, jstring jFilePath, jint widthPower, jint heightPower, jfloatArray jfs, jintArray jglArgs) {

	// 地图文件名字
	QL_FILE_PATH = _JString2CStr(env, jFilePath);

	// 设置宽高
	QL_WIDTH_POWER = widthPower;
	QL_WIDTH = 1 << QL_WIDTH_POWER;
	QL_WIDTH_MASK = QL_WIDTH - 1;
	QL_HEIGHT_POWER = heightPower;
	QL_HEIGHT = 1 << QL_HEIGHT_POWER;
	QL_HEIGHT_MASK = QL_HEIGHT - 1;
	QL_CELLS_COUNT = QL_WIDTH * QL_HEIGHT;
	printTime();
	printf("C init, QL_FILE_PATH= %s, width= %d, height= %d, QL_WIDTH_MASK=%d, QL_HEIGHT_MASK=%d, QL_WIDTH_POWER=%d, QL_HEIGHT_POWER=%d, QL_CELLS_COUNT=%d \n",
		QL_FILE_PATH, QL_WIDTH, QL_HEIGHT, QL_WIDTH_MASK, QL_HEIGHT_MASK, QL_WIDTH_POWER, QL_HEIGHT_POWER, QL_CELLS_COUNT);

	// 设置地图
	QL_MAP = (int*)malloc(sizeof(int) * QL_WIDTH * QL_HEIGHT);
	QL_FMAP = (char*)malloc(sizeof(char) * QL_WIDTH * QL_HEIGHT);

	// 设置加速度
	QL_ACCS_LENGTH = (*env)->GetArrayLength(env, jfs);
	QL_ACCS = (*env)->GetFloatArrayElements(env, jfs, JNI_FALSE);
	printTime();
	printf("\taccelerates 加速度数组=");
	for (int i = 0; i < QL_ACCS_LENGTH; i++) printf("%.2f, ", QL_ACCS[i]);
	printf("\n");

	// 设置 OpenGL 的参数
	int* glArgs = (*env)->GetIntArrayElements(env, jglArgs, JNI_FALSE);
	QL_PLANTFORM_IDX = glArgs[0];
	QL_DEVICE_IDX = glArgs[1];
	QL_GLOBAL_WORK_SIZE = glArgs[2];
	QL_LOCAL_WORK_SIZE = glArgs[3];
	QL_V_FACTOR = glArgs[4];
	printf("\tQL_PLANTFORM_IDX=%d,QL_DEVICE_IDX=%d,QL_GLOBAL_WORK_SIZE=%d,QL_LOCAL_WORK_SIZE=%d,QL_V_FACTOR=%d\n",
		QL_PLANTFORM_IDX, QL_DEVICE_IDX, QL_GLOBAL_WORK_SIZE, QL_LOCAL_WORK_SIZE, QL_V_FACTOR);


	init();


	return 0;
}
/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    evolution		演化，计算下一张地图
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_evolution
(JNIEnv* env, jobject self) {
	if(MY_DEBUG) printf("evolution 演化\n");
	evolution();
	return 0;
}

/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    save			保存地图到文件
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_save
(JNIEnv* env, jobject self) {
	saveToFile();
	return 0;
}

/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    close			保存地图，清理关闭
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_close
(JNIEnv* env, jobject self) {
	printf("close 关闭, ...\n");
	Java_icu_lingjie_jni_LingJieByCl_save(env, self);
	clean();
	printf("close 关闭, OK\n");
	return 0;
}

/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    getPoint		获取点
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_getPoint
(JNIEnv* env, jobject self, jint x, jint y) {
	int idx = getIdx(x, y);
	int result = QL_MAP[idx];
	if (MY_DEBUG) printf("setForce 获取点, x=%d, y=%d, idx=%d, cell=%d\n", x, y, idx, result);
	return result;
}

/*
 * Class:     icu_lingjie_jni_LingJieByCl
 * Method:    setForce		设置力
 * Signature: (IIII)I
 */
JNIEXPORT jint JNICALL Java_icu_lingjie_jni_LingJieByCl_setForce
(JNIEnv* env, jobject self, jint x, jint y, jint fx, jint fy) {
	// for (int i = 0; i < QL_CELLS_COUNT; i++) printf("%d,", QL_FMAP[i]); printf("\n");
	int idx = getIdx(x, y);
	char f = (char)((x & QL_F_MASK) << 4 | (y & QL_F_MASK));
	QL_FMAP[idx] = f;
	if (MY_DEBUG) printf("setForce 设置力, x=%d, y=%d, fx=%d, fy=%d, idx=%d, f=%d\n", x, y, fx, fy, idx, f);
	// for (int i = 0; i < QL_CELLS_COUNT; i++) printf("%d,", QL_FMAP[i]); printf("\n");
	return 0;
}


